using System;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using EasyDapr.Domain;
using EasyDapr.Infrastructure.App.Dtos;
using EasyDapr.Infrastructure.EfCore;
using EasyDapr.Infrastructure.Helpers;
using EasyDapr.Infrastructure.Response;
using InventoryService.Application.Requests;
using InventoryService.Domain.Event;
using InventoryService.Domain.Exception;
using InventoryService.Domain.Model;
using InventoryService.Infrastructure.Data;
using InventoryService.Infrastructure.Gateway;
using MediatR;
using Microsoft.EntityFrameworkCore;

namespace InventoryService.Application
{
    /// <summary>
    /// 库存请求处理程序
    /// </summary>
    public class InventoryRequestHandler :
        IRequestHandler<CreateInventoryRequest, ApiResult<bool>>,
        IRequestHandler<GetInventoryRequest, ApiResult<InventoryDto>>
    {
        private readonly IDbContextFactory<MainDbContext> _dbContextFactory;
        private readonly IMediator _mediator;
        private readonly DaprIdentityGateway _identityGateway;

        public InventoryRequestHandler(
            IDbContextFactory<MainDbContext> dbContextFactory,
            IMediator mediator,
            DaprIdentityGateway identityGateway)
        {
            _dbContextFactory = dbContextFactory ?? throw new ArgumentNullException(nameof(dbContextFactory));
            _mediator = mediator;
            _identityGateway = identityGateway;
        }

        /// <summary>
        /// 创建库存
        /// </summary>
        /// <param name="request"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<ApiResult<bool>> Handle(CreateInventoryRequest request, CancellationToken cancellationToken)
        {
            return await ResultHelper.CommonResultAsync(async () =>
            {
                // 创建上下文对象
                await using var dbContext = _dbContextFactory.CreateDbContext();
                // 使用事务
                await _mediator.HandleTransaction(dbContext, cancellationToken,
                    async () =>
                    {
                        var user = await _identityGateway.GetUserByIdAsync(request.UserId, cancellationToken);
                        if (!user.Success)
                        {
                            throw new CoreException("找不到用户信息.");
                        }

                        var entity = new Inventory
                        {
                            Location = request.Location,
                            Website = request.Website,
                            Description = $"{request.Description},用户信息：{JsonSerializer.Serialize(user.Data)}"
                        };
                        // 添加领域事件
                        entity.AddDomainEvent(
                            new InventoryCreatedEvent(
                                request.Location, 
                                request.Website,
                                entity.Description));

                        await dbContext.Inventories.AddAsync(entity, cancellationToken);
                        await dbContext.SaveChangesAsync(cancellationToken);
                        return entity;
                    });
                return true;
            });
        }

        /// <summary>
        /// 读取库存信息
        /// </summary>
        /// <param name="request"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<ApiResult<InventoryDto>> Handle(GetInventoryRequest request,
            CancellationToken cancellationToken)
        {
            return await ResultHelper.CommonResultAsync(async () =>
            {
                await using var dbContext = _dbContextFactory.CreateDbContext();

                var inv = await dbContext.Inventories
                    .AsNoTracking()
                    .FirstOrDefaultAsync(x => x.Id == request.Id, cancellationToken);

                if (inv == null)
                {
                    throw new NotFoundInventoryException(id: request.Id);
                }

                return new InventoryDto
                {
                    Id = inv.Id, Location = inv.Location, Description = inv.Description, Website = inv.Website
                };
            });
        }
    }
}